﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Data.SqlServerCe;
using System.Linq.Expressions;
using System.Data.Objects;
using System.Data;
using System.Diagnostics;
using System.ServiceModel.Channels;
using SalientBrain.Util.Linq;

namespace NoResponseAlarm.Service
{
    public static class ExtensionMethods
    {
        public static TResult NextId<TSource, TResult>(this ObjectQuery<TSource> table, Expression<Func<TSource, TResult>> selector) where TSource : class
        {
            TResult lastId = table.Any() ? table.Max(selector) : default(TResult);
            if (lastId is int)
            {
                lastId = (TResult)(object)(((int)(object)lastId) + 1);
            }
            return lastId;
        }
        /*        
        public static Int32 GetNextID<T>(this ObjectSet<T> objects) where T : class, IID
        {
            T entry = objects.OrderByDescending(u => u.ID).FirstOrDefault();
            if (entry == default(T))
                return 1;
            return entry.ID + 1;
        }
        */
    }
    
    //Microsoft SQL Server Compact 4.0
    //http://www.microsoft.com/download/en/details.aspx?id=17876

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class NoResponseRegistrar : INoResponseRegistrar
    {

        internal class FieldDef
        {
            public string Name { get; set; }
            public int Index { get; set; }
            public int Ordinal { get; set; }
            public string DataTypeName { get; set; }
            public int MaxLength { get; set; }
        }
        private TimeSpan CheckEventsControlPeriodLength = TimeSpan.FromMinutes(15);

        private static string GetClientIP()
        {
            MessageProperties properties = OperationContext.Current.IncomingMessageProperties;
            RemoteEndpointMessageProperty endpoint = properties[RemoteEndpointMessageProperty.Name]
                as RemoteEndpointMessageProperty;
            return endpoint.Address;
        }

        public event EventHandler EventRegistered;
        #region INoResponseRegistrar Members

        protected virtual void OnRegisterEvent(EventArgs e)
        {
            if (EventRegistered != null)
            {
                EventRegistered(this, e);
            }
        }
        
        public CheckEvent AlarmRequaired(CheckEventData eventData, TimeSpan controlPeriod, CheckResult[] checkResults) //AlertRequaired
        {
            using (NoResponseAlarmEntities ctx = new NoResponseAlarmEntities())
            {
                return null;
                /*
                ctx.CheckEvents.Where(evt => 
                    (evt.EventTimeStamp - eventData.TimeStamp) < controlPeriod
                    && evt.CheckTarget == eventData.
                    );*/
            }
        }
        
        //string extensions to implement: quoteIdent
        public void RegisterEvent(CheckEventData eventData)
        {
            try
            {
                //throw new NotImplementedException();
                var basePath = AppDomain.CurrentDomain.BaseDirectory;
                var fileName = @"C:\Dev\GameLand\NoResponseAlarm.Service\NoResponseAlarm.sdf";
                string dataSource = "Data Source=" + fileName;

                var tableName = "CheckEvent"; //checkEventTableName
                var identityFieldName = "EventID";
                var checkSourceFieldName = "CheckSource";
                var checkTargetFieldName = "CheckTarget";
                var eventTypeFieldName = "EventType";
                var eventTimeStampFieldName = "EventTimeStamp";
                int? id = null;

                var fieldNames = new string[] { identityFieldName, eventTypeFieldName, checkSourceFieldName, checkTargetFieldName, eventTimeStampFieldName, "CheckUser", "EventExtras" };

                using (SqlCeConnection connection = new SqlCeConnection(dataSource))
                {
                    connection.Open();

                    FieldDef[] fieldDefs = fieldNames.Select((name, index) => new FieldDef() { Index = index, Name = name, MaxLength = 0 }).ToArray(); //?
                    using (SqlCeCommand cmd = new SqlCeCommand(tableName, connection))
                    {
                        cmd.CommandType = CommandType.TableDirect;
                        using (SqlCeResultSet resultSet = cmd.ExecuteResultSet(ResultSetOptions.Updatable))
                        {
                            SqlCeUpdatableRecord record = resultSet.CreateRecord();

                            foreach (FieldDef fieldDef in fieldDefs)
                            {
                                fieldDef.Ordinal = record.GetOrdinal(fieldDef.Name);
                                fieldDef.DataTypeName = record.GetDataTypeName(fieldDef.Ordinal);
                            }

                            if (eventData.OperationResult == CheckResult.RequestSucceed)
                            {
                                //readerCmd
                                using (SqlCeCommand selectCmd = new SqlCeCommand(string.Format("SELECT {0} FROM {1} WHERE {2} = {3} AND {4} = {5} AND {6} = {7}", identityFieldName, tableName, checkSourceFieldName, eventData.Source.QuoteSQLLiteral(), checkTargetFieldName, eventData.Target.QuoteSQLLiteral(), eventTypeFieldName, ((byte)CheckResult.RequestSucceed)).QuoteSQLLiteral(), connection))
                                {
                                    using (SqlCeDataReader reader = selectCmd.ExecuteReader(CommandBehavior.SingleRow))
                                    {
                                        while (reader.Read())
                                        {
                                            id = reader.GetInt32(0);
                                        }
                                    }
                                }
                                if (id == null)
                                {
                                    using (SqlCeCommand insertCmd = new SqlCeCommand(string.Format("INSERT INTO {0} ({1}) VALUES ({2})", tableName,
                                                                            fieldNames.Skip(1).Join(","),
                                                                            string.Join(",", new string[] { ((byte)CheckResult.RequestSucceed).ToString(), eventData.Source.QuoteSQLLiteral(), eventData.Target.QuoteSQLLiteral(), eventData.TimeStamp.QuoteSQLLiteral(), eventData.UserName.QuoteSQLLiteral(), eventData.Extras.QuoteSQLLiteral() })), connection))
                                    {
                                        insertCmd.ExecuteNonQuery();
                                    }
                                }
                                else
                                {
                                    using (SqlCeCommand updateCmd = new SqlCeCommand(string.Format("UPDATE {0} SET {1} = {2} WHERE {3} = {4}", tableName, eventTimeStampFieldName, eventData.TimeStamp.QuoteSQLLiteral(), identityFieldName, id.QuoteSQLLiteral()), connection))
                                    {
                                        updateCmd.ExecuteNonQuery();
                                    }
                                }
                            }
                            else
                            {

                                /*
                                int i = 0;
                                using (SqlCeCommand schemaCmd = new SqlCeCommand(string.Format("SELECT character_maximum_length FROM information_schema.columns WHERE table_name = '{0}' AND column_name IN ({1})", tableName, string.Join(",", fieldNames.Select(name => name.QuoteSQLLiteral())), connection))
                                {
                                    using (SqlCeDataReader reader = schemaCmd.ExecuteReader(CommandBehavior.Default))
                                    {
                                        while (reader.Read())
                                        {
                                            if (!reader.IsDBNull(0))
                                            {
                                                fieldDefs[i].MaxLength = reader.GetInt32(0);
                                            }
                                            i++;
                                        }
                                    }
                                }
                                */



                                //Stopwatch sw = new Stopwatch();
                                //sw.Start();

                                InsertCheckEventRecord(fieldDefs,
                                    new Dictionary<string, object>() { { "EventType",(byte)eventData.OperationResult },
                                        {"CheckSource", eventData.Source},
                                        {"CheckTarget", eventData.Target},
                                        {"CheckUser", "me"},
                                        {"EventTimeStamp", eventData.TimeStamp},
                                        {"EventExtras", "extras"}},
                                    record,
                                    resultSet);

                                //sw.Stop();

                                //string ExecutionTimeTaken = string.Format("Minutes :{0}\nSeconds :{1}\nMiliseconds:{2}", sw.Elapsed.Minutes, sw.Elapsed.Seconds, sw.Elapsed.TotalMilliseconds);                           

                            }
                        }
                    }

                }

                /*
                var ctx = new NoResponseAlarmEntities(); //connectionString
                var checkEvent = new CheckEvent();

                checkEvent.EventID = ctx.CheckEvents.NextId<CheckEvent, int>(entity => entity.EventID);
                checkEvent.EventType = (byte)eventData.OperationResult;
                checkEvent.CheckSource = eventData.Source;
                checkEvent.CheckTarget = eventData.Target;
                checkEvent.EventTimeStamp = eventData.TimeStamp;
                checkEvent.CheckUser = "";
                checkEvent.EventExtras = "";
                
                ctx.AddToCheckEvents(checkEvent);
                ctx.SaveChanges();
                */

                OnRegisterEvent(EventArgs.Empty);
            }
            catch (Exception ex)
            {
                //throw;
            }
        }
        static private void InsertCheckEventRecord(FieldDef[] fieldDefs, Dictionary<string, object> fieldValues, SqlCeUpdatableRecord record, SqlCeResultSet resultSet)
        {

            //string tmp;            
            //string[] values = StringFunctions.Split(tmp, ',', '"', true);

            foreach (FieldDef fieldDef in fieldDefs)
            {
                object value;
                //if(fieldValues.ContainsKey(fieldDef.Name))
                if (fieldValues.TryGetValue(fieldDef.Name, out value))
                {
                    record.SetValue(fieldDef.Ordinal, value);
                }
                /*
                switch (fieldDef.DataTypeName)
                {
                    case "nvarchar":
                        if (fieldDef.MaxLength > 0 && values[fieldDef.Index].Length > fieldDef.MaxLength)
                        {
                            record.SetString(fieldDef.Ordinal, values[fieldDef.Index].Substring(0, fieldDef.MaxLength - 1));
                        }
                        else
                        {
                            record.SetString(fieldDef.Ordinal, values[fieldDef.Index]);
                        }
                        break;
                    case "float":
                        record.SetDouble(fieldDef.Ordinal, double.Parse(values[fieldDef.Index]));
                        break;
                    default:
                        record.SetValue(fieldDef.Ordinal, values[fieldDef.Index]);
                        break;
                }
                */

            }
            resultSet.Insert(record);

        }

        #endregion

    }
}
